package com.lin.mr.maizilaundry.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * 流式布局
 一 、流式布局特点、应用场景
 二、自定义ViewGroup
 1、onMeasure：测量子View的宽、高，设置自己的宽和高。
 2、onLayout:设置子View的位置。
 onMeasure：根据子View的布局文件，为子View设置测量模式和测量值。
 测量 = 测量模式+测量值；
 测量模式： 3种
 1、EXACTLY:100dp,match_parent
 2、AT_MOST:warp_content
 3、UNSPCIFIED:子控件想要多大就多大，很少见

 ViewGroup - LayoutParams
 （子）View.getLayoutParams();->（父）View(LinearLayout).LayoutParams
 */
public class FlowLayout extends ViewGroup {

    /**
     * 用于代码创建控件
     * @param context
     */
    public FlowLayout(Context context) {
        this(context,null);
    }
    /**
     *用于在xml里使用, 可指定自定义属性
     * @param context
     * @param attrs
     */
    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    /**
     * 用于在xml里使用, 可指定自定义属性, 如果指定了样式, 则走此构造函数
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //widthMeasureSpec包含了测量方法和测量值
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获得它的父容器为它设置的测量模式和大小
        //父容器为match_parent时，可以直接获得宽高
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
        Log.e("FlowLayout", sizeWidth + "," + sizeHeight);

        // 如果是warp_content情况下，需要自行计算和记录宽和高
        //这里的width和height指的是所有的关键词加起来作为一个整体时的宽高
        int width = 0;
        int height = 0;

        // 记录每一行的宽度与高度
        int lineWidth = 0;
        int lineHeight = 0;

        //得到内部元素的个数
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            //测量子View的宽和高
            measureChild(child,widthMeasureSpec,heightMeasureSpec);
            //得到LayoutParams
            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();

            //子View实际占据的宽度
            int childWidth = child.getMeasuredWidth()+layoutParams.leftMargin+layoutParams.rightMargin;
            //子View实际占据的高度
            int childHeight = child.getMeasuredHeight()+ layoutParams.topMargin+layoutParams.bottomMargin;

            /**
             * 如果加入当前的child之后超出最大宽度，则把得到的目前最大宽度给width，类加height 然后开启新行
             * 注意：如果布局设置了padding，那每一行还要减去左右的padding
             */
            if (lineWidth + childWidth > sizeWidth- getPaddingLeft() - getPaddingRight()){ //此时需要换行
                width = Math.max(width,lineWidth);//对比之后取最大值
                //重置lineWidth
                lineWidth = childWidth;
                //叠加行高
                height += lineHeight;
                lineHeight  = childHeight;
            }else { //不需要换行时
                //叠加行宽
                lineWidth += childWidth;
                //对比得到当前最大的高度
                lineHeight = Math.max(lineHeight, childHeight);
            }
            /**
             * 最后一个控件特殊处理：
             * 假如需要换行，计算的width和height是除了最后一行之外的宽高，应该再叠加一次
             * 假如不需要换行，else代码块只计算了最后一行的宽高lineWidth和lineHeight，所以还是需要更新宽高
             */
            if (i == count-1){
                width = Math.max(lineWidth,width);
                height += lineHeight;
            }
        }

//        if (modeWidth == MeasureSpec.AT_MOST){
//            setMeasuredDimension(width,height);//计算得到的宽高
//        }else if (modeWidth == MeasureSpec.EXACTLY){
//            setMeasuredDimension(sizeWidth,sizeHeight);
//        }
        //上面的if语句也可以这样写
        setMeasuredDimension(
                //使用三目运算符判断
                modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),
                modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop()+ getPaddingBottom()//
        );

        Log.e("FlowLayout", "sizeWidth = " + sizeWidth);
        Log.e("FlowLayout", "sizeHeight = " + sizeHeight);

        Log.e("FlowLayout", "width = " + width);
        Log.e("FlowLayout", "height = " + height);
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 存储所有的View,以行为单位
     */
    private List<List<View>> mAllViews = new ArrayList<List<View>>();
    /**
     * 每一行的高度
     */
    private List<Integer> mLineHeight = new ArrayList<Integer>();

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mAllViews.clear();
        mLineHeight.clear();

        //当前ViewGroup的宽度
        int width = getWidth();

        int lineWidth = 0;
        int lineHeight = 0;

        List<View> lineViews = new ArrayList<View>();
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            //需要换行
            if (childWidth + lineWidth + lp.leftMargin+lp.rightMargin >
                    width- getPaddingLeft() - getPaddingRight()){
                //记录行高
                mLineHeight.add(lineHeight);
                //记录当前的Views
                mAllViews.add(lineViews);
                // 重置行宽和行高
                lineWidth = 0;
                lineHeight = childHeight + lp.topMargin + lp.bottomMargin;

                // 重置View集合
                lineViews = new ArrayList<View>();
            }else {
                //不需要换行时
                lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
                lineHeight = Math.max(lineHeight,childHeight+lp.topMargin+lp.bottomMargin);
            }
            lineViews.add(child);

        }
        // 处理最后一行，直接添加行高
        mLineHeight.add(lineHeight);
        mAllViews.add(lineViews);

        //设置子View的位置
        int left = getPaddingLeft();
        int top = getPaddingTop();

        //行数
        int lineNum = mAllViews.size();

        for (int i = 0; i < lineNum; i++) { //遍历布局中的每一行
            lineViews = mAllViews.get(i);
            lineHeight = mLineHeight.get(i);

            for (int j = 0; j < lineViews.size(); j++) { //遍历每一行中的元素
                View child = lineViews.get(j);
                //判断标签的状态
                if (child.getVisibility()==View.GONE){
                    continue;
                }

                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                //标签的上下左右
                int lc = left + lp.leftMargin;
                int tc = top + lp.topMargin;
                int rc = lc + child.getMeasuredWidth();
                int bc = tc + child.getMeasuredHeight();

                //为标签进行布局
                child.layout(lc,tc,rc,bc);
                //从左往右标签的左边距依次叠加
                left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            }
            //遍历完一行之后让左边距归零
            left = getPaddingLeft();
            top += lineHeight;
        }
    }

    /**
     * 与当前ViewGroup对应的LayoutParams
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(),attrs);
    }

}
